package com.wans.guli.order.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.wans.guli.common.utils.R;
import com.wans.guli.order.entity.OrderItemEntity;
import com.wans.guli.order.enume.OrderStatusEnum;
import com.wans.guli.order.feign.CartFeignClient;
import com.wans.guli.order.feign.MemberFeignClient;
import com.wans.guli.order.feign.ProductFeignClient;
import com.wans.guli.order.feign.WareFeignClient;
import com.wans.guli.order.interceptor.OrderUserLoginCheckInterceptor;
import com.wans.guli.order.vo.*;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wans.guli.common.utils.PageUtils;
import com.wans.guli.common.utils.Query;

import com.wans.guli.order.dao.OrderDao;
import com.wans.guli.order.entity.OrderEntity;
import com.wans.guli.order.service.OrderService;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;


@Service("orderService")
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    private static final ThreadLocal<Long> addressIdLocal = new ThreadLocal<>();

    @Autowired
    private Executor executor;
    @Autowired
    private CartFeignClient cartFeignClient;
    @Autowired
    private MemberFeignClient memberFeignClient;
    @Autowired
    private ProductFeignClient productFeignClient;
    @Autowired
    private WareFeignClient wareFeignClient;
    @Autowired
    private RedissonClient redisson;
    @Autowired
    private OrderDao orderDao;
    @Autowired
    private OrderItemServiceImpl orderItemService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RabbitTemplate rabbitTemplate;


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 获取订单确认页数据
     *
     * @return 订单确认页数据
     */
    @Override
    @SneakyThrows
    public OrderConfirmVo getOrderConfirmData() {
        // 返回的确认信息数据
        OrderConfirmVo orderConfirm = new OrderConfirmVo();

        // 获取该次请求的用户信息
        MemberVO member = OrderUserLoginCheckInterceptor.memberInfoThreadLocal.get();

        // 异步获取用户地址信息
        CompletableFuture<Void> futureMemberAddressVos = CompletableFuture.supplyAsync(() -> {
            // 远程调用获取用户地址信息 - member服务
            R addressById = memberFeignClient.addressById(member.getId());
            List<MemberAddressVo> addressByIdListData = addressById.getListData(MemberAddressVo.class);
            // 设置用户信息
            orderConfirm.setMemberAddressVos(addressByIdListData);

            return addressByIdListData.stream()
                    .filter(address -> address.getDefaultStatus() == 1).findFirst()
                    .orElse(addressByIdListData.get(0));
        }, executor).thenAcceptAsync(memberAddressVo -> {
            // 获取地址id
            Long memberAddressId = memberAddressVo.getId();
            if (ObjectUtil.isNotEmpty(addressIdLocal.get())) memberAddressId = addressIdLocal.get(); // 使用本地线程中的
            // 远程调用获取运费信息
            R fareToR = wareFeignClient.getFare(memberAddressId);
            FareVo fareVo = fareToR.getData(FareVo.class);
            orderConfirm.setFare(fareVo.getFare());
        });

        // 获取上下文请求
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes());
        // 异步获取用户购物车信息
        CompletableFuture<Void> futureCartItems = CompletableFuture.supplyAsync(() -> {
            // 设置线程上下文属性
            RequestContextHolder.setRequestAttributes(servletRequestAttributes);
            // 远程调用获取用户购物车选中的skuId列表 - cart服务
            R skuIdsToR = cartFeignClient.getSkuIds();
            // 清除线程上下文请求属性
            RequestContextHolder.resetRequestAttributes();
            // 获取用户选中的购物项skuIds
            List<OrderItemVo> orderItemVos = skuIdsToR.getListData(OrderItemVo.class);
            // 映射 - 用户购物项映射的数据 - skuId为key, OrderItemVo为value
            return orderItemVos.stream().collect(Collectors.toMap(OrderItemVo::getSkuId, item -> item));
        }, executor).thenAcceptAsync(cartItems -> {

            List<Long> skuIds = cartItems.values().stream().map(OrderItemVo::getSkuId).collect(Collectors.toList());

            // 异步获取商品信息
            CompletableFuture<Void> futureItems = CompletableFuture.runAsync(() -> {
                // 远程调用根据skuId列表查询商品的所有信息 - product服务
                R skuByIds = productFeignClient.list(skuIds);
                List<SkuInfoVO> skuByIdsListData = skuByIds.getListData(SkuInfoVO.class);
                // 更新数据
                for (SkuInfoVO sku : skuByIdsListData) {
                    OrderItemVo orderItemVo = cartItems.get(sku.getSkuId());
                    if (ObjectUtil.isNotEmpty(orderItemVo)) {
                        // 获取价格 - 订单存在
                        orderItemVo.setPrice(sku.getPrice());
                    } else {
                        // 订单可能已经失效
                        cartItems.remove(sku.getSkuId());
                    }
                }
                // 设置订单项数据
                orderConfirm.setItems(new ArrayList<>(cartItems.values()));
            }, executor);

            // 异步获取商品库存状态
            CompletableFuture<Void> futureStocks = CompletableFuture.runAsync(() -> {
                // 远程调用根据skuId列表查询商品库存信息 - ware服务
                R stocksToR = wareFeignClient.stockBySkuIds(skuIds);
                Map<Long, Boolean> stocks = stocksToR.getData(new TypeReference<Map<Long, Boolean>>() {
                });
                orderConfirm.setStocks(stocks);
            });

            // 等待所有异步线程执行完成
            try {
                CompletableFuture.allOf(futureItems, futureStocks).get();
            } catch (InterruptedException | ExecutionException e) {
                throw new RuntimeException("异步 - futureItems - futureStocks 出现错误", e);
            }
        });

        // 等待所有异步线程执行完成
        CompletableFuture.allOf(futureMemberAddressVos, futureCartItems).get();
        // 返回数据
        return orderConfirm;
    }

    /**
     * 创建订单
     *
     * @param orderId 订单ID
     * @return 创建结果
     */
    @Override
    public SubmitOrderResponseVo submitOrder(String orderId, Long addressId) {
        // TODO 可以优化异步调用 - 不做了 心累

        // 创建返回结果
        SubmitOrderResponseVo submitOrderResponse = new SubmitOrderResponseVo();
        // 订单ID为空 - 没有需要支付的订单
        if (StrUtil.isBlank(orderId)) return submitOrderResponse;

        // 加锁 - 订单ID作为锁
        RLock lock = redisson.getLock("order:lock:order:skuId:" + orderId);
        boolean triedLock = lock.tryLock();
        if (Boolean.FALSE.equals(triedLock)) {
            // 获取锁失败
            return submitOrderResponse;
        }

        // 获取锁成功 - 开始创建订单
        // 获取该次请求的用户信息
        MemberVO member = OrderUserLoginCheckInterceptor.memberInfoThreadLocal.get();

        // 创建订单
        OrderEntity orderEntity = new OrderEntity();
        // 设置订单号
        orderEntity.setOrderSn(orderId);
        // 设置用户信息
        orderEntity.setMemberId(member.getId());
        orderEntity.setMemberUsername(member.getUsername());


        // 获取订单数据
        addressIdLocal.set(addressId);
        OrderConfirmVo orderConfirm = getOrderConfirmData();
        addressIdLocal.remove();
        // 设置订单数据
        orderEntity.setTotalAmount(orderConfirm.getTotal());
        orderEntity.setFreightAmount(orderConfirm.getFare());
        orderEntity.setPayAmount(orderConfirm.getPayPrice());
        orderEntity.setSourceType(1);
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        // 设置发票数据
        orderEntity.setBillType(0);
        orderEntity.setBillHeader("发票抬头");
        orderEntity.setBillContent("发票正文");
        // 设置接收发票的人的数据 - 收票人（使用收货人数据）
        orderEntity.setBillReceiverPhone(member.getMobile());
        orderEntity.setBillReceiverEmail(member.getEmail());
        // 设置收货人信息
        orderEntity.setReceiverName(member.getNickname());
        orderEntity.setReceiverPhone(member.getMobile());
        MemberAddressVo memberAddress = orderConfirm.getMemberAddressVos().stream()
                .filter(address -> Objects.equals(address.getId(), addressId))
                .findFirst().orElse(orderConfirm.getMemberAddressVos().get(0));
        orderEntity.setReceiverPostCode(memberAddress.getPostCode());
        orderEntity.setReceiverProvince(memberAddress.getProvince());
        orderEntity.setReceiverCity(memberAddress.getCity());
        orderEntity.setReceiverRegion(memberAddress.getRegion());
        orderEntity.setReceiverDetailAddress(memberAddress.getDetailAddress());
        // 修改时间
        orderEntity.setModifyTime(new Date());
        // 其他默认数据
        orderEntity.setNote("this is a 默认备注");

        // 锁定库存信息 - 根据skuId进行锁库存
        Map<Long, Long> skuIdAndNum = orderConfirm.getItems().stream()
                .filter(Objects::nonNull)
                .filter(item -> orderConfirm.getStocks().get(item.getSkuId()))
                .collect(Collectors.toMap(OrderItemVo::getSkuId, item -> Long.valueOf(item.getCount())));
        List<RLock> lockSkuIds = new ArrayList<>();
        // 获取上下文请求
        ServletRequestAttributes servletRequestAttributes2 =
                (ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes());
        skuIdAndNum.keySet().forEach(skuId -> {
            // 有库存的全部上锁
            RLock lockSkuId = redisson.getLock("order:lock:stock:skuId:" + skuId);
            lockSkuId.lock(); // 必须等
            lockSkuIds.add(lockSkuId);
        });
        // 远程调用减库存
        wareFeignClient.stockBySkuIdsSubStock(skuIdAndNum);

        // 插入数据库
        orderDao.insert(orderEntity);
        // 向 order.ttl 发送一个消息
        sendOrderMessage(orderEntity);
        // 设置订单关闭字符串
        submitOrderResponse.setTimeoutStr("30分钟");

        //保存 OrderItemEntity 数据
        List<Long> skuIds = orderConfirm.getItems().stream().map(OrderItemVo::getSkuId).collect(Collectors.toList());
        R spuInfoBySkuIdToR = productFeignClient.getSpuInfoBySkuId(skuIds);
        Map<Long, SpuInfoItemVO> spuInfoBySkuIdToRData = spuInfoBySkuIdToR.getData(new TypeReference<Map<Long, SpuInfoItemVO>>() {
        });
        List<OrderItemEntity> orderItemEntities = orderConfirm.getItems().stream().map(item -> {
            SpuInfoItemVO spuInfoItem = spuInfoBySkuIdToRData.get(item.getSkuId());
            OrderItemEntity orderItemEntity = new OrderItemEntity();
            orderItemEntity.setOrderId(orderEntity.getId());
            orderItemEntity.setOrderSn(orderEntity.getOrderSn());
            orderItemEntity.setSpuId(spuInfoItem.getSpuId());
            orderItemEntity.setSpuName(spuInfoItem.getSpuName());
            orderItemEntity.setSpuPic(spuInfoItem.getPrice());
            orderItemEntity.setSpuBrand(spuInfoItem.getSpuBrand());
            orderItemEntity.setCategoryId(spuInfoItem.getCategoryId());
            orderItemEntity.setSkuId(item.getSkuId());
            orderItemEntity.setSkuName(item.getTitle());
            orderItemEntity.setSkuPic(item.getImage());
            orderItemEntity.setSkuPrice(item.getPrice());
            orderItemEntity.setSkuQuantity(item.getCount());
            orderItemEntity.setSkuAttrsVals(String.join(";", item.getSkuAttrValues()));
            return orderItemEntity;
        }).collect(Collectors.toList());
        orderItemService.saveBatch(orderItemEntities);

        // 删除订单号
        stringRedisTemplate.delete("token:order:id:" + member.getId());
        // 解锁
        lockSkuIds.forEach(Lock::unlock);
        lock.unlock();

        // 添加response数据
        submitOrderResponse.setOrder(orderEntity);
        submitOrderResponse.setCode(0);

        return submitOrderResponse;
    }

    /**
     * 发送订单消息
     *
     * @param order 订单
     */
    public void sendOrderMessage(OrderEntity order) {
        try {
            // 创建包含订单号的 CorrelationData
            CorrelationData correlationData = new CorrelationData(order.getOrderSn());
            // 发送消息到交换机
            rabbitTemplate.convertAndSend(
                    "order.create",
                    "order.ttl",
                    order,
                    message -> {
                        // 设置消息属性
                        message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
                        message.getMessageProperties().setContentType("application/json");
                        return message;
                    },
                    correlationData
            );

            log.info(String.format("订单消息发送到交换机-订单号-> %s", order.getOrderSn()));

        } catch (Exception e) {
            log.error(String.format("订单消息发送到交换机失败-订单号-> %s", order.getOrderSn()));
            log.error("发送订单消息异常: ", e);
            // 处理异常情况
        }
    }
}